﻿// <copyright file="TrackPlayer.cs" company="Microsoft">
// Copyright (c) 2009 Microsoft All Rights Reserved
// </copyright>
// <author>Microsoft</author>
// <email>mischero@microsoft.com</email>
// <date>2009-01-01</date>
// <summary>Web analytics service behavior class definition</summary>

namespace Microsoft.WebAnalytics.Media
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    using Microsoft.SilverlightMediaFramework.Logging;
    using Microsoft.SilverlightMediaFramework.Player;
    using Microsoft.WebAnalytics;
    using Microsoft.WebAnalytics.Data;

    /// <summary>
    /// <img src="../media/Microsoft.WebAnalytics.Media.TrackPlayer.24x24.png"/> 
    /// Track Log events on a Microsoft Silverlight Media Framework Player
    /// </summary>
    /// <remarks>The <a href="http://codeplex.com/smf">Microsoft Silverlight Media Framework</a>
    /// Player exposes these events:
    /// <list type="table">
    /// <listheader>
    /// <term>Message</term>
    /// <description>Properties</description>
    /// </listheader>
    /// <item>
    ///     <term>PlaybackBitrateChanged</term>
    ///     <description>AnalyticsEvent.Value: Playback Bitrate</description>
    /// </item>
    /// <item>
    ///     <term>MediaOpened</term>
    ///     <description>AnalyticsEvent.ContentId: Clip URL; AnalyticsEvent.ActionValue: Clip URL </description>
    /// </item>
    /// <item>
    ///     <term>BufferingStatusChanged</term>
    ///     <description>AnalyticsEvent.Value: Buffering Progress (0-100)</description>
    /// </item>
    /// <item>
    ///     <term>ScrubStarted</term>
    ///     <description>AnalyticsEvent.Value: media position in seconds</description>
    /// </item>
    /// <item>
    ///     <term>ScrubCompleted</term>
    ///     <description>AnalyticsEvent.Value: media position in seconds</description>
    /// </item>
    /// <item>
    ///     <term>PlayStateChanged</term>
    ///     <description>AnalyticsEvent.ContentId: Clip URL; AnalyticsEvent.Value: Current State from SmoothStreamingMediaElementState</description>
    /// </item>
    /// <item>
    ///     <term>FullScreenChanged</term>
    ///     <description>AnalyticsEvent.Value: 1 for full screen, 0 for windowed</description>
    /// </item>
    /// </list>
    /// </remarks>
    public class TrackPlayer : Behavior<Player>
    {
        #region Fields
        /// <summary>
        /// the current volume level (0-1)
        /// </summary>
        private double volumeLevel;
        #endregion

        /// <summary>
        /// Initializes a new instance of the TrackPlayer class
        /// </summary>
        public TrackPlayer()
        {
            this.CustomProperties = new Collection<PropertyValue>();
        }

        #region Properties

        /// <summary>
        /// Gets the current MediaElement State
        /// </summary>
        public System.Windows.Media.MediaElementState CurrentClipState
        {
            get
            {
                var cssme = this.AssociatedObject.MediaElement;

                if (cssme == null)
                {
                    return MediaElementState.Closed;
                }

                try
                {
                    System.Diagnostics.Debug.WriteLine("Current Playback State: {0}", cssme.CurrentPlaybackState);

                    int state = (int)cssme.CurrentPlaybackState;

                    return (MediaElementState)Enum.ToObject(typeof(MediaElementState), state);
                }
                catch (System.Exception se)
                {
                    System.Diagnostics.Debug.WriteLine(se.Message);

                    return MediaElementState.Closed;
                }
            }
        }

        /// <summary>
        /// Gets custom properties (name/value pairs) for the action
        /// </summary>
        public Collection<PropertyValue> CustomProperties { get; private set; }

        /// <summary>
        /// Gets or sets the event Logging method
        /// </summary>
        /// <remarks>This is imported from the Microsoft.WebAnalytics assembly using MEF.</remarks>
        [Import("Log")]
        public Action<AnalyticsEvent> Log { get; set; }
        #endregion

        #region Methods
        /// <summary>
        /// Attach events and initialize parts
        /// </summary>
        /// <remarks>This will also attach an event handler to the Logger.LogReceived event.</remarks>
        protected override void OnAttached()
        {
            base.OnAttached();

            CompositionInitializer.SatisfyImports(this);

            Logger.LogReceived += new EventHandler<Microsoft.SilverlightMediaFramework.SimpleEventArgs<Log>>(this.Logger_LogReceived);

            this.AssociatedObject.PlayStateChanged += new PlayStateChangedRoutedEventHandler(this.PlayStateChanged);

            this.volumeLevel = this.AssociatedObject.VolumeLevel;
        }

        /// <summary>
        /// Detach events
        /// </summary>
        /// <remarks>This will also detach the event handler from the Logger.LogReceived event.</remarks>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            Logger.LogReceived -= new EventHandler<Microsoft.SilverlightMediaFramework.SimpleEventArgs<Log>>(this.Logger_LogReceived);
            this.AssociatedObject.PlayStateChanged -= new PlayStateChangedRoutedEventHandler(this.PlayStateChanged);
        }

        #endregion

        #region Implementation
        /// <summary>
        /// Modify the logEvent based on the type of message
        /// </summary>
        /// <param name="message">the message</param>
        /// <param name="cssme">the Smooth Streaming Media Element</param>
        /// <param name="clipUrl">the Clip URL</param>
        /// <param name="logEvent">the log event</param>
        private static void ModifyEvent(string message, CoreSmoothStreamingMediaElement cssme, Uri clipUrl, AnalyticsEvent logEvent)
        {
            logEvent.EventArgs.Add(new PropertyValue { PropertyName = "CurrentClipState", Value = cssme.CurrentPlaybackState.ToString() });

            var clipUrlString = (clipUrl == null) ? string.Empty : clipUrl.ToString();

            switch (message)
            {
                case "PlaybackBitrateChanged":
                    logEvent.Value = Convert.ToInt32(cssme.PlaybackBitrate);
                    break;

                case "MediaOpened":
                    logEvent.ContentId = clipUrlString;
                    logEvent.ActionValue = clipUrlString;
                    break;

                case "BufferingStatusChanged":
                    logEvent.Value = Convert.ToInt32(cssme.BufferingProgress * 100);
                    break;

                case "ScrubStarted":
                    logEvent.Value = System.Convert.ToInt32(Math.Round(cssme.Position.TotalSeconds));
                    break;

                case "ScrubCompleted":
                    logEvent.Value = System.Convert.ToInt32(Math.Round(cssme.Position.TotalSeconds));
                    break;

                case "PlayStateChanged":
                    logEvent.ContentId = clipUrlString;
                    logEvent.Value = (int)cssme.CurrentPlaybackState;
                    break;

                case "FullScreenChanged":
                    var host = Application.Current.Host;
                    var content = host.Content;
                    logEvent.Value = System.Convert.ToInt32(content.IsFullScreen);
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// Log a PlayStateChanged
        /// </summary>
        /// <param name="sender">the Player</param>
        /// <param name="e">the play state changed routed event arguments</param>
        private void PlayStateChanged(object sender, PlayStateChangedRoutedEventArgs e)
        {
            this.LogEvent("PlayStateChanged", e.CurrentPlayState.ToString());
        }

        /// <summary>
        /// Log Received event handler
        /// </summary>
        /// <param name="sender">the Logger</param>
        /// <param name="e">the log simple event argument</param>
        private void Logger_LogReceived(
            object sender, 
            Microsoft.SilverlightMediaFramework.SimpleEventArgs<Log> e)
        {
            this.LogEvent(e.Result.Message, e.Result.Message);
        }

        /// <summary>
        /// Log an event
        /// </summary>
        /// <param name="message">the message to log</param>
        /// <param name="actionValue">the action value</param>
        private void LogEvent(string message, string actionValue)
        {
            message = this.GetMessage(message, ref actionValue);

            var cssme = this.AssociatedObject.MediaElement;

            Uri clipUrl = null;

            if (cssme != null)
            {
                clipUrl = cssme.IsSmoothStream ? cssme.SmoothStreamingSource : cssme.Source;
            }

            Duration duration;

            try
            {
                // EstimatedDuration handles both live and on-demand content
                duration = new Duration(cssme.EstimatedDuration);
            }
            catch (InvalidOperationException)
            {
                // get the Duration throws an exception if there is an error
                duration = new Duration();
            }

            // Initialize the AnalyticsEvent with parameters specific to the control event
            var logEvent = new AnalyticsEvent()
            {
                ActionValue = actionValue,
                EventArgs = this.CustomProperties,
                Name = message,
                ObjectName = this.AssociatedObject.Name,
                ObjectType = this.AssociatedObject.GetType().ToString(),
                MediaTimeCode = cssme.Position,
                MediaSource = clipUrl,
                Clip = new Microsoft.WebAnalytics.Data.ClipContext()
                {
                    ClipInformation = new Microsoft.WebAnalytics.Data.ClipInformation()
                    {
                        ClipUrl = clipUrl,
                        IsSmoothStreamingSource = cssme.IsSmoothStream,
                        Duration = duration
                    },
                    CurrentClipState = this.CurrentClipState
                },
                Track = new Microsoft.WebAnalytics.Data.TrackInfo()
                {
                    Bitrate = Convert.ToInt64(cssme.PlaybackBitrate),
                    DroppedFramesPerSecond = cssme.DroppedFramesPerSecond
                }
            };

            ModifyEvent(message, cssme, clipUrl, logEvent);

            // Log the event to the Silverlight Analytics Framework
            this.Log(logEvent);
        }

        /// <summary>
        /// Get the message name
        /// </summary>
        /// <param name="message">the message name from the SMF</param>
        /// <param name="actionValue">the action value to split out of the message</param>
        /// <returns>a message name for the SAF (possibly modified)</returns>
        private string GetMessage(string message, ref string actionValue)
        {
            if (message == "VolumeLevelChanged")
            {
                if (this.volumeLevel > this.AssociatedObject.VolumeLevel)
                {
                    message = "VolumeDecrease";
                }
                else if (this.volumeLevel < this.AssociatedObject.VolumeLevel)
                {
                    message = "VolumeIncrease";
                }

                this.volumeLevel = this.AssociatedObject.VolumeLevel;

                actionValue = this.AssociatedObject.VolumeLevel.ToString(CultureInfo.InvariantCulture);
            }
            else if (message.StartsWith("PlaybackBitrateChanged:", StringComparison.OrdinalIgnoreCase))
            {
                message = "PlaybackBitrateChanged";
            }
            else if (message.StartsWith("MediaFailed", StringComparison.OrdinalIgnoreCase))
            {
                actionValue = message.Substring(13);
                message = "MediaFailed";
            }
            else if (message.StartsWith("MediaRetry", StringComparison.OrdinalIgnoreCase))
            {
                actionValue = message.Substring(11);
                message = "MediaRetry";
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("SMF Message: {0}", message);
            }

            return message;
        }
        #endregion
    }
}
